package small.common.designPattern.structure.proxy.cglib;


import small.common.designPattern.structure.proxy.AdvisedSupport;
import small.common.designPattern.structure.proxy.AopProxy;
import small.common.designPattern.structure.proxy.ReflectiveMethodInvocation;
import small.common.designPattern.structure.proxy.TargetSource;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @Author zhoujin
 * @Date 2022/6/26 15:47
 */
public class Cglib2AopProxy implements AopProxy {

    public static final Cglib2AopProxy CGLIB_PROXY = new Cglib2AopProxy();

    private Cglib2AopProxy() {
    }

    @Override
    public <T> T getProxy(AdvisedSupport<T> adviseSupport) {
        Enhancer enhancer = new Enhancer();
        TargetSource<T> targetSource = adviseSupport.getTargetSource();
        enhancer.setSuperclass(targetSource.getTargetClass());
        enhancer.setInterfaces(targetSource.getInterfaces());
        enhancer.setCallback(new DynamicAdvisedInterceptor(adviseSupport));
        return (T) enhancer.create();
    }

    private static class DynamicAdvisedInterceptor implements MethodInterceptor {
        private final AdvisedSupport advised;

        public DynamicAdvisedInterceptor(AdvisedSupport advised) {
            this.advised = advised;
        }

        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            TargetSource targetSource = advised.getTargetSource();

            CglibMethodInvocation methodInvocation = new CglibMethodInvocation(targetSource.getTarget(),targetSource.getTargetClass(), method, objects, methodProxy);
            if (advised.getMethodMatcher() != null) {
                if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTargetClass())){
                    return advised.getAopMethodInterceptor().invoke(methodInvocation);
                }else {
                    if (targetSource.getTarget() == null) {
                        return null;
                    }
                    //执行原来方法
                    return methodInvocation.proceed();
                }

            }
            return advised.getAopMethodInterceptor().invoke(methodInvocation);
        }
    }


    //Cglib代理方法
    private static class CglibMethodInvocation extends ReflectiveMethodInvocation {

        private final MethodProxy methodProxy;

        public CglibMethodInvocation(Object target,Class<?> targetClass, Method method, Object[] arguments, MethodProxy methodProxy) {
            super(target,targetClass, method, arguments);
            this.methodProxy = methodProxy;
        }

        @Override
        public Object proceed() throws Throwable {
            if (target == null) {
                return null;
            }
            return this.methodProxy.invoke(this.target, this.arguments);
        }

    }
}
